using System;
using System.Collections.Generic;
using System.Linq;
using TestCases.Utils;
using TestCases.Utils.Pairs;

namespace TestCases.CompareAssert.Matchers
{
    public class IndexesMatcher: IObjectMatcher<long[]>, IObjectMatcher<long>
    {

        private readonly HashSet<Predicate<long[]>> _indexesPredicates = new HashSet<Predicate<long[]>>(PredicateEqualityComparer<long[]>.Instance);
        
        private readonly HashSet<IPair<IObjectMatcher<int>, IObjectMatcher<long>>> _indexMatchers = 
            new HashSet<IPair<IObjectMatcher<int>, IObjectMatcher<long>>>();

        public void AddPredicate(Predicate<long[]> predicate)
        {
            _indexesPredicates.Add(predicate);
        }
        
        public void AddMatcher(IObjectMatcher<int> dimensionMatcher, IObjectMatcher<long> indexMatcher)
        {
            _indexMatchers.Add(new Pair<IObjectMatcher<int>, IObjectMatcher<long>>(dimensionMatcher, indexMatcher));
        }

        public void Reset()
        {            
            _indexesPredicates.Clear();
            _indexMatchers.Clear();
        }

        public bool Equals(IndexesMatcher other)
        {
            return other != null
                && (other == this
                    || (_indexMatchers.SetEquals(other._indexMatchers)
                        && _indexesPredicates.SetEquals(other._indexesPredicates)));
        }

        public bool IsMatch(long[] indexes)
        {
            if (_indexesPredicates.FirstOrDefault(x => x(indexes)) != null)
            {
                return true;
            }
            
            for (var i=0; i<indexes.Length; i++)
            {
                int dimension = i;
                if (_indexMatchers.FirstOrDefault(x => x.Left.IsMatch(dimension) && x.Right.IsMatch(indexes[dimension])) != null)
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsMatch(long value)
        {
            return (_indexesPredicates.FirstOrDefault(x => x(new[] {value})) != null)
                || (_indexMatchers.FirstOrDefault(x => x.Left.IsMatch(0) && x.Right.IsMatch(value)) != null);
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as IndexesMatcher);
        }

        public override int GetHashCode()
        {
            return _indexesPredicates.CalculateHash() ^ _indexMatchers.CalculateHash();
        }
    }
}